﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Engine.Sgml.XPath {
    internal static class XPathBuilder {
        private static StringBuilder builder = new StringBuilder();
        private static List<string> names = new List<string>();

        public static string BuildAbsolute(SElement element) {
            Guard.ArgumentNotNull(element, "element");
            names.Clear();
            SElement node = element;
            while (node != null && node.NodeType != SgmlNodeType.Document) {
                AddObjectName(GetObjectName(node), GetElementPosition(node));
                node = node.Parent;
                }
            names.Reverse();
            return string.Join("/", names);
            }

        public static string BuildAbsolute(SAttribute attribute) {
            Guard.ArgumentNotNull(attribute, "element");
            names.Clear();
            AddObjectName(GetObjectName(attribute), GetAttributePosition(attribute));
            SElement node = attribute.Parent;
            while (node != null && node.NodeType != SgmlNodeType.Document) {
                AddObjectName(GetObjectName(node), GetElementPosition(node));
                node = node.Parent;
                }
            names.Reverse();
            return string.Join("/", names);
            }

        private static int GetAttributePosition(SAttribute attribute) {
            int position = 1;
            SAttribute previous = attribute.PreviousAttribute;
            while (previous != null) {
                if (attribute.Name == previous.Name)
                    position++;
                previous = previous.PreviousAttribute;
                }
            return position;
            }

        private static void AddObjectName(string name, int position) {
            if (position == 1)
                names.Add(name);
            else
                names.Add(string.Format("{0}[{1}]", name, position));
            }

        private static int GetElementPosition(SElement node) {
            int position = 1;
            SElement previous = node.PreviousNonInclusionNode();
            if (node.NodeType == SgmlNodeType.Element)
                while (previous != null) {
                    if (node.Name == previous.Name && previous.NodeType == SgmlNodeType.Element)
                        position++;
                    previous = previous.PreviousNonInclusionNode();
                    }
            else {
                while (previous != null) {
                    if (node.NodeType == previous.NodeType)
                        position++;
                    previous = previous.PreviousNonInclusionNode();
                    }
                }
            return position;
            }

        private static string GetObjectName(SObject node) {
            switch (node.NodeType) {
                case SgmlNodeType.Attribute:
                    return string.Format("@{0}", node.Name.ToString());
                case SgmlNodeType.Element:
                    return node.Name.ToString();
                case SgmlNodeType.Text:
                    return "text()";
                default:
                    throw new InvalidOperationException(string.Format("Could not build path for the '{0}' element.", node.NodeType.ToString()));
                }
            }

        public static string BuildRelative(SElement element) {
            builder.Clear();
            builder.Append('.');
            builder.Append(Characters.FractionSlash);
            builder.Append(Characters.FractionSlash);
            builder.Append(element.Name.ToString());
            if (element.HasAttributes) {
                builder.Append(Characters.LeftBracket);
                SAttribute attribute = element.FirstAttribute;
                do {
                    builder.Append('@');
                    builder.Append(attribute.Name.ToString());
                    builder.Append(Characters.Whitespace);
                    builder.Append(Characters.EqualsChar);
                    builder.Append(Characters.Whitespace);
                    builder.Append(Characters.Apostrophe);
                    builder.Append(attribute.Value);
                    builder.Append(Characters.Apostrophe);
                    attribute = attribute.NextAttribute;
                    if (attribute == null)
                        break;
                    builder.Append(Characters.Whitespace);
                    builder.Append('a');
                    builder.Append('n');
                    builder.Append('d');
                    builder.Append(Characters.Whitespace);
                    } while (true);
                builder.Append(Characters.RightBracket);
                }
            return builder.ToString();
            }
        }
    }
